package com.jt.www.util;

import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.Stack;

/**
 * 描述：表达式结果计算工具类 <br>
 * 类名称：RPNExpressCalcuteUtils..java <br>
 * 作者： xjl <br>
 * 版本：1.0 <br>
 * 修改日期：2018/7/10 10:36 <br>
 * 创建日期：2018/7/9 17:58 <br>
 * 版权：江泰保险经纪股份有限公司 <br>
 */
public class RPNCalcuteUtils {

    /**
     * 数字正则
     */
    private static final String NUMREGEX = "^(-?\\d+)(\\.\\d+)?$";
    /**
     * 小数点保留位数
     */
    private static final Integer RETENTIONNUM = 30;

    /**
     * 运算符栈，用于临时存储运算符的栈（其中含有一个结束符号）
     */
    private Stack<String> operatorStack = new Stack<String>();
    /**
     * 逆波兰式栈，用于存储逆波兰式
     */
    private Stack<String> reversePolishNotationStack = new Stack<String>();

    public RPNCalcuteUtils() {
        // 默认放入优先级最低的 # 号表示结束
        this.operatorStack.push("#");
    }

    /**************************************************************************************************************************************************************************************/

    public static void main(String[] args) {

        RPNCalcuteUtils RPNCU = new RPNCalcuteUtils();
        System.out.println("=====================================");
        new Thread() {
            @Override
            public void run() {
                String expression = "8/4+4*0.08";
                System.out.println("源计算式：" + expression);
                System.out.println("逆波兰式：" + RPNCU.RPNExpression(expression));
                System.out.println("计算结果：" + RPNCU.calculateRPNExpress(expression));
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                String expression = "8/4+4*0.1";
                System.out.println("源计算式：" + expression);
                System.out.println("逆波兰式：" + RPNCU.RPNExpression(expression));
                System.out.println("计算结果：" + RPNCU.calculateRPNExpress(expression));
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                String expression = "8/4+4*0.02";
                System.out.println("源计算式：" + expression);
                System.out.println("逆波兰式：" + RPNCU.RPNExpression(expression));
                System.out.println("计算结果：" + RPNCU.calculateRPNExpress(expression));
            }
        }.start();
        System.out.println("=====================================");

    }

    /**************************************************************************************************************************************************************************************/

    /**
     * 可用额度计算
     *
     * @param rateJson：计算公式
     * @param basic：计算基数
     * @return
     */
    public BigDecimal availableCredit(String rateJson, BigDecimal basic) {
        if (basic.compareTo(BigDecimal.ZERO) < 0) {
            return this.calculateRPNExpress(MessageFormat.format(rateJson.replaceAll(" ", ""), basic.negate().toString())).negate();
        } else {
            return this.calculateRPNExpress(MessageFormat.format(rateJson.replaceAll(" ", ""), basic.toString()));
        }
    }

    /**************************************************************************************************************************************************************************************/

    /**
     * 获取逆波兰表达式
     */
    private synchronized String RPNExpression(String intermediateOrderExpress) {

        // 解析波兰式
        this.RPNParse(intermediateOrderExpress.replaceAll(" ", "") + "#");
        StringBuilder stringbuilder = new StringBuilder();
        for (int index = 0; index < this.reversePolishNotationStack.size(); index++) {
            stringbuilder.append(this.reversePolishNotationStack.get(index));
        }
        return stringbuilder.toString();

    }

    /**************************************************************************************************************************************************************************************/

    /**
     * 计算逆波兰式的结果
     *
     * @param intermediateOrderExpress：中序表达式
     * @return
     */
    private synchronized BigDecimal calculateRPNExpress(String intermediateOrderExpress) {

        // 解析波兰式
        this.RPNParse(intermediateOrderExpress.replaceAll(" ", "") + "#");
        // 依据波兰式计算计算式子结果
        BigDecimal result = new BigDecimal("0");
        // 计算栈
        Stack<String> calculateStack = new Stack<String>();
        for (int index = 0; index < this.reversePolishNotationStack.size(); index++) {
            String item = this.reversePolishNotationStack.get(index);
            if (item.matches(NUMREGEX)) {
                calculateStack.push(item);
            } else {
                BigDecimal oerandSecond = new BigDecimal(calculateStack.pop());
                BigDecimal operandFirst = new BigDecimal(calculateStack.pop());
                BigDecimal res = calculate(operandFirst, oerandSecond, item);
                calculateStack.push(res.toString());
            }
        }
        if (!calculateStack.isEmpty()) {
            return new BigDecimal(calculateStack.pop());
        }
        return result;

    }

    /**
     * 解析逆波兰式栈
     *
     * @param intermediateOrderExpress
     */
    private synchronized void RPNParse(String intermediateOrderExpress) {

        // 清空波兰式栈
        this.reversePolishNotationStack.clear();
        StringBuilder stringBuilder = new StringBuilder("");
        for (int index = 0; index < intermediateOrderExpress.length(); index++) {
            String curChar = String.valueOf(intermediateOrderExpress.charAt(index));
            if (curChar.matches(NUMREGEX) || ".".equals(curChar)) {
                stringBuilder.append(curChar);
            } else {
                if (!StringUtils.isBlank(stringBuilder)) {
                    this.reversePolishNotationStack.push(stringBuilder.toString());
                }
                stringBuilder = new StringBuilder("");
                operatorCompare(curChar);
            }
        }

    }

    /**
     * 当前运算符与操作符栈中的运算符进行比较 <br>
     * （1）若取出的字符是运算符，则将该运算符与 OPERATOR_STACK 栈栈顶元素比较，如果该运算符优先级（不包括“(”、“)”）大于
     * OPERATOR_STACK 栈栈顶运算符优先级，则将该运算符进 OPERATOR_STACK 栈， 否则，将 OPERATOR_STACK
     * 栈的栈顶运算符弹出，送入 RPN_STACK 栈中，直至 OPERATOR_STACK
     * 栈栈顶运算符低于（不包括等于）该运算符优先级，最后将该运算符送入S1栈。<br>
     * （3）若取出的字符是“(”，则直接送入 OPERATOR_STACK 栈顶 <br>
     * （4）若取出的字符是“)”，则将距离 OPERATOR_STACK 栈栈顶最近的“(”之间的运算符，逐个出栈，依次送入S2栈，此时抛弃“（” <br>
     *
     * @param operator
     */
    private synchronized void operatorCompare(String operator) {

        String top = null;
        switch (operator) {
            case "(":
                // 操作符进入操作符栈
                this.operatorStack.push(operator);
                break;
            case ")":
                // 将操作符栈中的操作符依次弹出，添加到逆波兰式栈中，直到遇到第一个“(”时停止，并且抛弃“(”
                top = this.operatorStack.pop();
                while (!"(".equals(top)) {
                    this.reversePolishNotationStack.push(top);
                    top = this.operatorStack.pop();
                }
                break;
            default:
                int curLev = operatorPrecedence(operator);
                top = this.operatorStack.pop();
                int topLev = operatorPrecedence(top);
                while (true) {
                    // 如果当前运算符优先级大于
                    if ((curLev > topLev) || "(".equals(top)) {
                        this.operatorStack.push(top);
                        this.operatorStack.push(operator);
                        break;
                    } else {
                        this.reversePolishNotationStack.push(top);
                        top = this.operatorStack.pop();
                        if ("#".equals(top)) {
                            this.operatorStack.push(top);
                            this.operatorStack.push(operator);
                            break;
                        }
                        topLev = operatorPrecedence(top);
                    }
                }
                break;
        }

    }

    /**
     * 获取运算符的优先级
     *
     * @param operator：运算符
     * @return 运算符等级
     */
    private int operatorPrecedence(String operator) {
        int level = 0;
        switch (operator) {
            case "+":
            case "-":
                level = 1;
                break;
            case "*":
            case "/":
                level = 2;
                break;
            case "(":
            case ")":
                level = 3;
                break;
            default:
                break;
        }
        return level;
    }

    /**
     * 计算操作
     *
     * @param operandFirst：操作数——1
     * @param operandSecond：操作数——2
     * @param operator：运算符
     * @return 计算结果保留 4 位小数
     */
    private BigDecimal calculate(BigDecimal operandFirst, BigDecimal operandSecond, String operator) {

        switch (operator) {
            case "+":
                return operandFirst.add(operandSecond).setScale(RETENTIONNUM);
            case "-":
                return operandFirst.subtract(operandSecond).setScale(RETENTIONNUM);
            case "*":
                return operandFirst.multiply(operandSecond).setScale(RETENTIONNUM);
            case "/":
                return operandFirst.divide(operandSecond).setScale(RETENTIONNUM);
            default:
                return new BigDecimal(0).setScale(RETENTIONNUM);
        }

    }

}
